/**
 * sl.js v1.1.0
 * (c) 2019-present fineDay
 * ISC License.
 * @preserve
 */

(function (global, factory) {
    console.log(global)
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory()
        : typeof define === 'function' && define.amd ? define(factory)
        : (global.sl = factory())
}(this, function () {
    'use strict'

    var formatString = 'yyyy-MM-dd HH:mm:ss'
    var setupDefaults = {
        treeOptions: {
            parentKey: 'parentId',
            key: 'id',
            children: 'children'
        },
        formatDate: formatString + '.SSSZ',
        formatString: formatString,
        dateDiffRules: [
            ['yyyy', 31536000000],
            ['MM', 2592000000],
            ['dd', 86400000],
            ['HH', 3600000],
            ['mm', 60000],
            ['ss', 1000],
            ['S', 0]
        ]
    }

    function mixin() {
        arrayEach(arguments, function (methods) {
            each(methods, function (fn, name) {
                sl[name] = isFunction(fn) ? function () {
                    var result = fn.apply(sl.$context, arguments)
                    sl.$context = null
                    return result
                } : fn
            })
        })
    }

    function setup(options) {
        assign(setupDefaults, options)
    }

    function sl() {
    }

    sl.v = 'v1.1.0';
    sl.mixin = mixin;
    sl.setup = setup;
    var staticStrUndefined = 'undefined';
    var staticParseInt = parseInt;
    var objectAssignFns = Object.assign;
    let requestList = {}; //api请求记录
    var staticDecodeURIComponent = decodeURIComponent
    var staticEncodeURIComponent = encodeURIComponent
    /* eslint-disable valid-typeof */
    var staticLocation = typeof location === staticStrUndefined ? 0 : location

    /* eslint-disable valid-typeof */
    var staticWindow = typeof window === staticStrUndefined ? 0 : window

    /* eslint-disable valid-typeof */
    var staticDocument = typeof document === staticStrUndefined ? 0 : document

    var staticHGKeyRE = /(.+)?\[(\d+)\]$/

    var staticEscapeMap = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#x27;',
        '`': '&#x60;'
    }

    function handleAssign(destination, args, isClone) {
        var len = args.length
        for (var source, index = 1; index < len; index++) {
            source = args[index]
            arrayEach(keys(args[index]), isClone ? function (key) {
                destination[key] = clone(source[key], isClone)
            } : function (key) {
                destination[key] = source[key]
            })
        }
        return destination
    }

    /**
     * 指定方法后的返回值组成的新对象
     *
     * @param {Object} obj 对象/数组
     * @param {Function} iterate(item, index, obj) 回调
     * @param {Object} context 上下文
     * @return {Object}
     */
    function objectMap(obj, iterate, context) {
        var result = {}
        if (obj) {
            if (iterate) {
                if (!isFunction(iterate)) {
                    iterate = property(iterate)
                }
                each(obj, function (val, index) {
                    result[index] = iterate.call(context, val, index, obj)
                })
            } else {
                return obj
            }
        }
        return result
    }

    /**
     * 浅拷贝一个或者多个对象到目标对象中
     *
     * @param {Object} obj 目标对象
     * @param {...Object}
     * @return {Boolean}
     */
    var assign = function (target) {
        if (target) {
            var args = arguments
            if (target === true) {
                if (args.length > 1) {
                    target = isArray(target[1]) ? [] : {}
                    return handleAssign(target, args, true)
                }
            } else {
                return objectAssignFns ? objectAssignFns.apply(Object, args) : handleAssign(target, args)
            }
        }
        return target
    }

    // ------------------------------------  判断方法  ---------------------------------------
    /**
     * 判断对象自身属性中是否具有指定的属性
     *
     * @param {Object} obj 对象
     * @param {String/Number} key 键值
     * @return {Boolean}
     */
    function hasOwnProp(obj, key) {
        return obj.hasOwnProperty(key)
    }

    /**
     * 判断是否 undefined 和 null
     * @param {Object} obj 对象
     * @return {Boolean}
     */
    function eqNull(obj) {
        return isNull(obj) || isUndefined(obj)
    }

    /* eslint-disable eqeqeq */
    function isNumberNaN(obj) {
        return isNumber(obj) && isNaN(obj)
    }

    function isNumberFinite(obj) {
        return isNumber(obj) && isFinite(obj)
    }

    /**
     * 判断是否Undefined
     *
     * @param {Object} obj 对象
     * @return {Boolean}
     */
    var isUndefined = helperCreateInTypeof(staticStrUndefined)

    /**
     * 判断是否数组
     *
     * @param {Object} obj 对象
     * @return {Boolean}
     */
    var isArray = Array.isArray || helperCreateInInObjectString('Array')

    /**
     * 判断是否小数
     *
     * @param {Number} obj 数值
     * @return {Boolean}
     */
    function isFloat(obj) {
        return !isNull(obj) && !isNaN(obj) && !isArray(obj) && !isInteger(obj)
    }

    /**
     * 判断是否整数
     *
     * @param {Number, String} number 数值
     * @return {Boolean}
     */
    var isInteger = function (obj) {
        return !isNull(obj) && !isNaN(obj) && !isArray(obj) && obj % 1 === 0
    }

    /**
     * 判断是否方法
     *
     * @param {Object} obj 对象
     * @return {Boolean}
     */
    var isFunction = helperCreateInTypeof('function')

    /**
     * 判断是否Boolean对象
     *
     * @param {Object} obj 对象
     * @return {Boolean}
     */
    var isBoolean = helperCreateInTypeof('boolean')

    /**
     * 判断是否String对象
     *
     * @param {Object} obj 对象
     * @return {Boolean}
     */
    var isString = helperCreateInTypeof('string')

    /**
     * 判断是否Number对象
     *
     * @param {Object} obj 对象
     * @return {Boolean}
     */
    var isNumber = helperCreateInTypeof('number')
    /**
     * 判断是否RegExp对象
     *
     * @param {Object} obj 对象
     * @return {Boolean}
     */
    var isRegExp = helperCreateInInObjectString('RegExp')

    /**
     * 判断是否Object对象
     *
     * @param {Object} obj 对象
     * @return {Boolean}
     */
    var isObject = helperCreateInTypeof('object')

    /**
     * 判断是否对象
     *
     * @param {Object} obj 对象
     * @return {Boolean}
     */
    function isPlainObject(obj) {
        return obj ? obj.constructor === Object : false
    }

    /**
     * 判断是否Date对象
     *
     * @param {Object} obj 对象
     * @return {Boolean}
     */
    var isDate = helperCreateInInObjectString('Date')

    /**
     * 判断是否为空对象
     *
     * @param {Object} obj 对象
     * @return {Boolean}
     */
    function isEmpty(obj) {
        for (var key in obj) {
            return false
        }
        return true
    }

    /**
     * 判断是否为Null
     *
     * @param {Object} obj 对象
     * @return {Boolean}
     */
    function isNull(obj) {
        return obj === null
    }

    /**
     * 判断属性中的键和值是否包含在对象中
     *
     * @param {Object/Array} obj 对象
     * @param {Object} source 值
     * @return {Boolean}
     */
    function isMatch(obj, source) {
        var objKeys = keys(obj)
        var sourceKeys = keys(source)
        if (sourceKeys.length) {
            if (includeArrays(objKeys, sourceKeys)) {
                return some(sourceKeys, function (key2) {
                    return findIndexOf(objKeys, function (key1) {
                        return key1 === key2 && isEqual(obj[key1], source[key2])
                    }) > -1
                })
            }
        } else {
            return true
        }
        return isEqual(obj, source)
    }

    /**
     * 深度比较两个对象之间的值是否相等
     *
     * @param {Object} obj1 值1
     * @param {Object} obj2 值2
     * @return {Boolean}
     */
    function isEqual(obj1, obj2) {
        return helperEqualCompare(obj1, obj2, helperDefaultCompare)
    }

    /**
     * 深度比较两个对象之间的值是否相等，使用自定义比较函数
     *
     * @param {Object} obj1 值1
     * @param {Object} obj2 值2
     * @param {Function} func 自定义函数
     * @return {Boolean}
     */
    function isEqualWith(obj1, obj2, func) {
        if (isFunction(func)) {
            return helperEqualCompare(obj1, obj2, function (v1, v2, key, obj1, obj2) {
                var result = func(v1, v2, key, obj1, obj2)
                return isUndefined(result) ? helperDefaultCompare(v1, v2) : !!result
            }, func)
        }
        return helperEqualCompare(obj1, obj2, helperDefaultCompare)
    }

    /**
     * 判断车牌号
     * @param val
     * @returns {boolean}
     */
    function isCarNumber(val) {
        var express = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9]{1}$/;
        try {
            if (!express.test(val)) {
                return false;
            } else {
                return true;
            }
        } catch (e) {
            console.log(e);
            return false;
        }
    }

    /**
     * 判断手机号
     * @param item
     * @returns {boolean}
     */
    function isMobile(item) {
        var regx = /^1[0-9]{10}$/;
        return regx.test(item);
    }

    /**
     * 判断小数
     * @param val
     * @returns {boolean}
     */
    function isDecimals(val) {
        try {
            var regx = /^\d+\.{0,1}\d{0,2}$/;
            if (!regx.test(val)) {
                return false;
            } else {
                return true;
            }
        } catch (e) {
            console.log(e);
            return false;
        }
    }

    /**
     * 获取对象所有属性
     *
     * @param {Object} obj 对象/数组
     * @return {Array}
     */
    var keys = helperCreateGetObjects('keys', 1)
    /**
     * 获取一个全局唯一标识
     *
     * @param {String} prefix 前缀
     * @return {Number}
     */
    var __uniqueId = 0

    function uniqueId(prefix) {
        return (prefix ? '' + prefix : 0) + ++__uniqueId
    }

    /**
     * 返回对象的长度
     *
     * @param {Object} obj 对象
     * @return {Number}
     */
    function getSize(obj) {
        var len = 0
        if (isString(obj) || isArray(obj)) {
            return obj.length
        }
        each(obj, function () {
            len++
        })
        return len
    }

    /**
     * 返回对象第一个索引值
     *
     * @param {Object} obj 对象
     * @param {Object} val 值
     * @return {Number}
     */
    var indexOf = helperCreateIndexOf('indexOf', arrayIndexOf)
    /**
     * 返回对象第一个索引值
     *
     * @param {Object} obj 对象/数组
     * @param {Function} iterate(item, index, obj) 回调
     * @param {Object} context 上下文
     * @return {Object}
     */
    var findIndexOf = helperCreateiterateIndexOf(function (obj, iterate, context) {
        for (var index = 0, len = obj.length; index < len; index++) {
            if (iterate.call(context, obj[index], index, obj)) {
                return index
            }
        }
        return -1
    })
    /**
     * 根据 key 过滤指定的属性值，返回一个新的对象
     *
     * @param {Object} obj 对象
     * @param {String/Array} key 键数组
     * @return {Object}
     */
    var pick = helperCreatePickOmit(1, 0)

    /**
     * 迭代器
     *
     * @param {Object} obj 对象/数组
     * @param {Function} iterate(item, index, obj) 回调
     * @param {Object} context 上下文
     * @return {Object}
     */
    function each(obj, iterate, context) {
        if (obj) {
            return (isArray(obj) ? arrayEach : objectEach)(obj, iterate, context)
        }
        return obj
    }

    /**
     * 迭代器,支持 return false 跳出循环 break
     *
     * @param {Object} obj 对象/数组
     * @param {Function} iterate(item, index, obj) 回调
     * @param {Object} context 上下文
     * @return {Object}
     */
    function forOf(obj, iterate, context) {
        if (obj) {
            if (isArray(obj)) {
                for (var index = 0, len = obj.length; index < len; index++) {
                    if (iterate.call(context, obj[index], index, obj) === false) {
                        break
                    }
                }
            } else {
                for (var key in obj) {
                    if (hasOwnProp(obj, key)) {
                        if (iterate.call(context, obj[key], key, obj) === false) {
                            break
                        }
                    }
                }
            }
        }
    }

    /**
     * 迭代器,从最后开始迭代
     *
     * @param {Object} obj 对象/数组
     * @param {Function} iterate(item, index, obj) 回调
     * @param {Object} context 上下文
     * @return {Object}
     */
    function lastEach(obj, iterate, context) {
        if (obj) {
            return (isArray(obj) ? lastArrayEach : lastObjectEach)(obj, iterate, context)
        }
        return obj
    }

    /**
     * 判断数组是否包含另一数组
     *
     * @param {Array} array1 数组
     * @param {Array} array2 被包含数组
     * @return {Boolean}
     */
    function includeArrays(array1, array2) {
        var len
        var index = 0
        if (isArray(array1) && isArray(array2)) {
            for (len = array2.length; index < len; index++) {
                if (!includes(array1, array2[index])) {
                    return false
                }
            }
            return true
        }
        return includes(array1, array2)
    }

    /**
     * 数组去重
     *
     * @param {Array} array 数组
     * @return {Array}
     */
    function uniq(array) {
        var result = []
        each(array, function (value) {
            if (!includes(result, value)) {
                result.push(value)
            }
        })
        return result
    }

    /**
     * 将多个数的值返回唯一的并集数组
     *
     * @param {...Array} 数组
     * @return {Array}
     */
    function union() {
        var args = arguments
        var result = []
        var index = 0
        var len = args.length
        for (; index < len; index++) {
            result = result.concat(toArray(args[index]))
        }
        return uniq(result)
    }

    function sortByDef(v1, v2) {
        return v1 > v2 ? 1 : -1
    }

    function sortMultis(name, compares) {
        return function (item1, item2) {
            var v1 = item1[name]
            var v2 = item2[name]
            if (v1 === v2) {
                return compares ? compares(item1, item2) : 0
            }
            return sortByDef(v1, v2)
        }
    }

    function getSortPros(arr, list, iterate, context) {
        iterate = isArray(iterate) ? iterate : [iterate]
        arrayEach(iterate, function (prop, index) {
            arrayEach(list, isFunction(prop) ? function (val, key) {
                val[index] = prop.call(context, val.data, key, arr)
            } : function (val) {
                val[index] = get(val.data, prop)
            })
        })
        return iterate
    }

    /**
     * 数组按属性值升序
     *
     * @param {Array} arr 数组
     * @param {Function/String/Array} iterate 方法或属性
     * @param {Object} context 上下文
     * @return {Array}
     */
    function sortBy(arr, iterate, context, STR_UNDEFINED) {
        if (arr) {
            if (iterate === STR_UNDEFINED) {
                return toArray(arr).sort(sortByDef)
            }
            var compares
            var list = map(arr, function (item) {
                return {data: item}
            })
            var sortPros = getSortPros(arr, list, iterate, context)
            var len = sortPros.length
            if (len) {
                while (len >= 0) {
                    compares = sortMultis(len, compares)
                    len--
                }
                list = list.sort(compares)
            }
            return map(list, property('data'))
        }
        return []
    }

    /**
     * 查找匹配第一条数据
     *
     * @param {Object} obj 对象/数组
     * @param {Function} iterate(item, index, obj) 回调
     * @param {Object} context 上下文
     * @return {Object}
     */
    var find = helperCreateIterateHandle('find', 1, 3, true)

    /**
     * 裁剪 Arguments 或数组 array，从 start 位置开始到 end 结束，但不包括 end 本身的位置
     * @param {Array/Arguments} array 数组或Arguments
     * @param {Number} startIndex 开始索引
     * @param {Number} endIndex 结束索引
     */
    function slice(array, startIndex, endIndex) {
        var result = []
        if (array) {
            for (startIndex = startIndex || 0, endIndex = endIndex || array.length; startIndex < endIndex; startIndex++) {
                result.push(array[startIndex])
            }
        }
        return result
    }

    /**
     * 对象中的值中的每一项运行给定函数,如果函数对任一项返回true,则返回true,否则返回false
     *
     * @param {Object} obj 对象/数组
     * @param {Function} iterate(item, index, obj) 回调
     * @param {Object} context 上下文
     * @return {Boolean}
     */
    var some = helperCreateIterateHandle('some', 1, 0, true, false)

    /**
     * 对象中的值中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true,否则返回false
     *
     * @param {Object} obj 对象/数组
     * @param {Function} iterate(item, index, obj) 回调
     * @param {Object} context 上下文
     * @return {Boolean}
     */
    var every = helperCreateIterateHandle('every', 1, 1, false, true)

    function arrayIndexOf(obj, val) {
        if (obj.indexOf) {
            return obj.indexOf(val)
        }
        for (var index = 0, len = obj.length; index < len; index++) {
            if (val === obj[index]) {
                return index
            }
        }
    }

    /**
     * 判断对象是否包含该值,成功返回true否则false
     *
     * @param {Object} obj 对象
     * @param {Object} val 值
     * @return {Boolean}
     */
    function includes(obj, val) {
        return indexOf(obj, val) !== -1
    }

    /**
     * 指定方法后的返回值组成的新数组
     *
     * @param {Object} obj 对象/数组
     * @param {Function} iterate(item, index, obj) 回调
     * @param {Object} context 上下文
     * @return {Array}
     */
    function map(obj, iterate, context) {
        var result = []
        if (obj && arguments.length > 1) {
            if (!isFunction(iterate)) {
                iterate = property(iterate)
            }
            if (obj.map) {
                return obj.map(iterate, context)
            } else {
                each(obj, function () {
                    result.push(iterate.apply(context, arguments))
                })
            }
        }
        return result
    }

    /**
     * 求和函数，将数值相加
     *
     * @param {Array} array 数组
     * @param {Function/String} iterate 方法或属性
     * @param {Object} context 上下文
     * @return {Number}
     */
    function sum(array, iterate, context) {
        var result = 0
        each(array, iterate ? isFunction(iterate) ? function () {
            result += toNumber(iterate.apply(context, arguments))
        } : function (val) {
            result += toNumber(val[iterate])
        } : function (val) {
            result += toNumber(val)
        })
        return result
    }

    /**
     * 求平均值函数
     *
     * @param {Array} array 数组
     * @param {Function/String} iterate 方法或属性
     * @param {Object} context 上下文
     * @return {Number}
     */
    function mean(array, iterate, context) {
        return toNumber(sum(array, iterate, context) / getSize(array))
    }

    /**
     * 将每个数组中相应位置的值合并在一起
     *
     * @param {Array*} array 数组
     */
    function zip() {
        return unzip(arguments)
    }

    /**
     * 与 zip 相反
     *
     * @param {Array} arrays 数组集合
     */
    function unzip(arrays) {
        var index, maxItem, len
        var result = []
        if (arrays && arrays.length) {
            index = 0
            maxItem = max(arrays, function (item) {
                return item ? item.length : 0
            })
            for (len = maxItem ? maxItem.length : 0; index < len; index++) {
                result.push(map(arrays, index))
            }
        }
        return result
    }

    /**
     * 根据键数组、值数组对转换为对象
     *
     * @param {Array} props 键数组
     * @param {Number} arr 值数组
     * @return {Object}
     */
    function zipObject(props, arr) {
        var result = {}
        arr = arr || []
        each(values(props), function (val, key) {
            result[val] = arr[key]
        })
        return result
    }

    /**
     * 获取数组对象中某属性值，返回一个数组
     *
     * @param {Array} array 数组
     * @param {String} key 属性值
     * @return {Array}
     */
    function pluck(obj, key) {
        return map(obj, key)
    }

    /**
     * 数组迭代器
     *
     * @param obj
     * @param iterate
     * @param context
     */
    function arrayEach(obj, iterate, context) {
        if (obj) {
            if (obj.forEach) {
                obj.forEach(iterate, context)
            } else {
                for (var index = 0, len = obj.length; index < len; index++) {
                    iterate.call(context, obj[index], index, obj)
                }
            }
        }
    }

    function strictTree(array, optChildren) {
        each(array, function (item) {
            if (item.children && !item.children.length) {
                remove(item, optChildren)
            }
        })
    }

    /**
     * 从树结构中根据回调查找数据
     *
     * @param {Object} obj 对象/数组
     * @param {Function} iterate(item, index, items, path, parent, nodes) 回调
     * @param {Object} options {children: 'children'}
     * @param {Object} context 上下文
     * @return {Array}
     */
    var searchTree = helperCreateTreeFunc(function (parent, obj, iterate, context, path, nodes, parseChildren, opts) {
        return searchTreeItem(0, parent, obj, iterate, context, path, nodes, parseChildren, opts)
    })

    function helperCreateIterateHandle(prop, useArray, restIndex, matchValue, defaultValue) {
        return function (obj, iterate, context) {
            if (obj && iterate) {
                if (prop && obj[prop]) {
                    return obj[prop](iterate, context)
                } else {
                    if (useArray && isArray(obj)) {
                        for (var index = 0, len = obj.length; index < len; index++) {
                            if (!!iterate.call(context, obj[index], index, obj) === matchValue) {
                                return [true, false, index, obj[index]][restIndex]
                            }
                        }
                    } else {
                        for (var key in obj) {
                            if (hasOwnProp(obj, key)) {
                                if (!!iterate.call(context, obj[key], key, obj) === matchValue) {
                                    return [true, false, key, obj[key]][restIndex]
                                }
                            }
                        }
                    }
                }
            }
            return defaultValue
        }
    }

    function helperCreateTreeFunc(handle) {
        return function (obj, iterate, options, context) {
            var opts = options || {}
            var optChildren = opts.children || 'children'
            return handle(null, obj, iterate, context, [], [], optChildren, opts)
        }
    }

    function helperCreateGetObjects(name, getIndex) {
        var proMethod = Object[name]
        return function (obj) {
            var result = []
            if (obj) {
                if (proMethod) {
                    return proMethod(obj)
                }
                each(obj, getIndex > 1 ? function (key) {
                    result.push(['' + key, obj[key]])
                } : function () {
                    result.push(arguments[getIndex])
                })
            }
            return result
        }
    }

    /**
     * 将对象或者伪数组转为新数组
     *
     * @param {Array} obj 数组
     * @return {Array}
     */
    function toArray(array) {
        return map(array, function (item) {
            return item
        })
    }

    function arrayEach(obj, iterate, context) {
        if (obj) {
            if (obj.forEach) {
                obj.forEach(iterate, context)
            } else {
                for (var index = 0, len = obj.length; index < len; index++) {
                    iterate.call(context, obj[index], index, obj)
                }
            }
        }
    }

    function lastArrayEach(obj, iterate, context) {
        for (var len = obj.length - 1; len >= 0; len--) {
            iterate.call(context, obj[len], len, obj)
        }
    }

    function strictTree(array, optChildren) {
        each(array, function (item) {
            if (item.children && !item.children.length) {
                remove(item, optChildren)
            }
        })
    }

    /**
     * 将一个带层级的数据列表转成树结构
     *
     * @param {Array} array 数组
     * @param {Object} options {strict: false, parentKey: 'parentId', key: 'id', children: 'children', data: 'data'}
     * @return {Array}
     */
    function toArrayTree(array, options) {
        var opts = assign({}, setupDefaults.treeOptions, options)
        var optStrict = opts.strict
        var optKey = opts.key
        var optParentKey = opts.parentKey
        var optChildren = opts.children
        var optSortKey = opts.sortKey
        var optReverse = opts.reverse
        var optData = opts.data
        var result = []
        var treeMap = {}
        var idList, id, treeData, parentId

        if (optSortKey) {
            array = sortBy(clone(array), optSortKey)
            if (optReverse) {
                array = array.reverse()
            }
        }

        idList = map(array, function (item) {
            return item[optKey]
        })

        each(array, function (item) {
            id = item[optKey]

            if (optData) {
                treeData = {}
                treeData[optData] = item
            } else {
                treeData = item
            }

            parentId = item[optParentKey]
            treeMap[id] = treeMap[id] || []
            treeMap[parentId] = treeMap[parentId] || []
            treeMap[parentId].push(treeData)
            treeData[optKey] = id
            treeData[optParentKey] = parentId
            treeData[optChildren] = treeMap[id]

            if (!optStrict || (optStrict && !parentId)) {
                if (!includes(idList, parentId)) {
                    result.push(treeData)
                }
            }
        })

        if (optStrict) {
            strictTree(array, optChildren)
        }

        return result
    }


    /**
     * 获取对象的属性的值，如果值为 undefined，则返回默认值
     * @param {Object/Array} obj 对象
     * @param {String/Function} property 键、路径
     * @param {Object} defaultValue 默认值
     * @return {Object}
     */
    function get(obj, property, defaultValue) {
        if (eqNull(obj)) {
            return defaultValue
        }
        var result = pathGet(obj, property)
        return isUndefined(result) ? defaultValue : result
    }

    function valGet(obj, key) {
        var matchs = key ? key.match(staticHGKeyRE) : ''
        return matchs ? (matchs[1] ? (obj[matchs[1]] ? obj[matchs[1]][matchs[2]] : undefined) : obj[matchs[2]]) : obj[key]
    }

    function pathGet(obj, property) {
        if (obj) {
            var rest, props, len
            var index = 0
            if (obj[property] || hasOwnProp(obj, property)) {
                return obj[property]
            } else {
                props = helperGetHGSKeys(property)
                len = props.length
                if (len) {
                    for (rest = obj; index < len; index++) {
                        rest = valGet(rest, props[index])
                        if (eqNull(rest)) {
                            return
                        }
                    }
                }
                return rest
            }
        }
    }

    var sKeyRE = /(.+)\[(\d+)\]$/

    function valSet(obj, key, isSet, value) {
        if (obj[key]) {
            if (isSet) {
                obj[key] = value
            }
        } else {
            var index
            var matchs = key ? key.match(sKeyRE) : null
            var rest = isSet ? value : {}
            if (matchs) {
                index = staticParseInt(matchs[2])
                if (obj[matchs[1]]) {
                    obj[matchs[1]][index] = rest
                } else {
                    obj[matchs[1]] = new Array(index + 1)
                    obj[matchs[1]][index] = rest
                }
            } else {
                obj[key] = rest
            }
            return rest
        }
        return obj[key]
    }

    /**
     * 设置对象属性上的值。如果属性不存在则创建它
     * @param {Object/Array} obj 对象
     * @param {String/Function} property 键、路径
     * @param {Object} value 值
     */
    function set(obj, property, value) {
        if (obj) {
            if (obj[property] || hasOwnProp(obj, property)) {
                obj[property] = value
            } else {
                var rest = obj
                var props = helperGetHGSKeys(property)
                var len = props.length
                for (var index = 0; index < len; index++) {
                    rest = valSet(rest, props[index], index === len - 1, value)
                }
            }
        }
        return obj
    }

    function startClone(func, obj, deep) {
        return func(obj, deep ? function (val) {
            return deepClone(val, deep)
        } : function (val) {
            return val
        })
    }

    /**
     * 集合分组,默认使用键值分组,如果有iterate则使用结果进行分组
     *
     * @param {Array} obj 对象
     * @param {Function} iterate 回调/对象属性
     * @param {Object} context 上下文
     * @return {Object}
     */
    function groupBy(obj, iterate, context) {
        var groupKey
        var result = {}
        if (obj) {
            if (iterate && isObject(iterate)) {
                iterate = createiterateEmpty(iterate)
            } else if (!isFunction(iterate)) {
                iterate = property(iterate)
            }
            each(obj, function (val, key) {
                groupKey = iterate ? iterate.call(context, val, key, obj) : val
                if (result[groupKey]) {
                    result[groupKey].push(val)
                } else {
                    result[groupKey] = [val]
                }
            })
        }
        return result
    }

    /**
     * 浅拷贝/深拷贝
     *
     * @param {Object} obj 对象/数组
     * @param {Boolean} deep 是否深拷贝
     * @return {Object}
     */
    function clone(obj, deep) {
        if (obj) {
            return deepClone(obj, deep)
        }
        return obj
    }

    function pluckProperty(name) {
        return function (obj, key) {
            return key === name
        }
    }

    /**
     * 移除对象属性
     *
     * @param {Object/Array} obj 对象/数组
     * @param {Function/String} iterate 方法或属性
     * @param {Object} context 上下文
     * @return {Object/Array}
     */
    function remove(obj, iterate, context) {
        if (obj) {
            if (!eqNull(iterate)) {
                var removeKeys = []
                var rest = []
                if (!isFunction(iterate)) {
                    iterate = pluckProperty(iterate)
                }
                each(obj, function (item, index, rest) {
                    if (iterate.call(context, item, index, rest)) {
                        removeKeys.push(index)
                    }
                })
                if (isArray(obj)) {
                    lastEach(removeKeys, function (item, key) {
                        rest.push(obj[item])
                        obj.splice(item, 1)
                    })
                } else {
                    rest = {}
                    arrayEach(removeKeys, function (key) {
                        rest[key] = obj[key]
                        helperDeleteProperty(obj, key)
                    })
                }
                return rest
            }
            return clear(obj)
        }
        return obj
    }

    /**
     * 将一个或者多个对象值解构到目标对象
     *
     * @param {Object} destination 目标对象
     * @param {...Object}
     * @return {Boolean}
     */
    function destructuring(destination, sources) {
        if (destination && sources) {
            var rest = assign.apply(this, [{}].concat(slice(arguments, 1)))
            var restKeys = keys(rest)
            arrayEach(keys(destination), function (key) {
                if (includes(restKeys, key)) {
                    destination[key] = rest[key]
                }
            })
        }
        return destination
    }


    function helperGetHGSKeys(property) {
        // 以最快的方式判断数组，可忽略准确性
        return property ? (property.splice && property.join ? property : ('' + property).split('.')) : []
    }

    /**
     * 获取一个指定范围内随机数
     *
     * @param {Number} minVal 最小值
     * @param {Number} maxVal 最大值
     * @return {Number}
     */
    function random(minVal, maxVal) {
        return minVal >= maxVal ? minVal : ((minVal = minVal >> 0) + Math.round(Math.random() * ((maxVal || 9) - minVal)))
    }

    /**
     * 获取最小值
     *
     * @param {Array} arr 数组
     * @param {Function} iterate(item, index, obj) 回调
     * @return {Number}
     */
    var min = helperCreateMinMax(function (rest, itemVal) {
        return rest > itemVal
    })

    /**
     * 获取最大值
     *
     * @param {Array} arr 数组
     * @param {Function} iterate(item, index, obj) 回调
     * @return {Number}
     */
    var max = helperCreateMinMax(function (rest, itemVal) {
        return rest < itemVal
    })

    /**
     * 千分位分隔符、小数点
     *
     * @param {String/Number} num 数值
     * @param {Object} 参数 {spaceNumber: 分割位数(默认3), separator: 分隔符(默认,), fixed: 小数位数(默认null)}
     * @return {String}
     */
    function commafy(num, options) {
        num = toValString(num).replace(/,/g, '')
        if (num) {
            var opts = assign({spaceNumber: 3, separator: ','}, options)
            var optFixed = opts.fixed
            var result = (optFixed ? toNumber(num).toFixed(optFixed) : num).split('.')
            return result[0].replace(new RegExp('(?=(?!(\\b))(\\d{' + opts.spaceNumber + '})+$)', 'g'), opts.separator) + (result[1] ? '.' + result[1] : '')
        }
        return num
    }

    /**
     * 和 Number.toFixed 类似，区别就是不会对小数进行四舍五入，结果返回数值
     *
     * @param { String/Number } str 数值
     * @return {String}
     */
    function toFixedNumber(str, digits) {
        var rest = (digits ? toNumber : toInteger)(helperFixedNumber(str, digits))
        return rest === 0 ? 0 : rest
    }

    /**
     * 转数值
     * @param { String/Number } str 数值
     *
     * @return {Number}
     */
    var toNumber = helperCreateToNumber(parseFloat)

    /**
     * 转整数
     * @param { String/Number } str 数值
     *
     * @return {Number}
     */
    var toInteger = helperCreateToNumber(staticParseInt)

    /**
     * 获取指定位数随机数
     * @param n
     * @returns {string}
     */
    function randomNum(n) {
        var t = '';
        for (var i = 0; i < n; i++) {
            t += Math.floor(Math.random() * 10);
        }
        return t;
    }

    function parseURLQuery(uri) {
        return unserialize(uri.split('?')[1] || '')
    }

    /**
     * 解析url
     * @param url
     * @returns {{hostname: string, protocol: string, search: (*|string), port: string, host: string, href: *, hash: string}}
     */
    function parseUrl(url) {
        var hashs, portText, searchs, parsed
        var href = '' + url
        if (href.indexOf('//') === 0) {
            href = (staticLocation ? staticLocation.protocol : '') + href
        } else if (href.indexOf('/') === 0) {
            href = helperGetLocatOrigin() + href
        }
        searchs = href.replace(/#.*/, '').match(/(\?.*)/)
        parsed = {
            href: href,
            hash: '',
            host: '',
            hostname: '',
            protocol: '',
            port: '',
            search: searchs && searchs[1] && searchs[1].length > 1 ? searchs[1] : ''
        }
        parsed.path = href.replace(/^([a-z0-9.+-]*:)\/\//, function (text, protocol) {
            parsed.protocol = protocol
            return ''
        }).replace(/^([a-z0-9.+-]*)(:\d+)?\/?/, function (text, hostname, port) {
            portText = port || ''
            parsed.port = portText.replace(':', '')
            parsed.hostname = hostname
            parsed.host = hostname + portText
            return '/'
        }).replace(/(#.*)/, function (text, hash) {
            parsed.hash = hash.length > 1 ? hash : ''
            return ''
        })
        hashs = parsed.hash.match(/#((.*)\?|(.*))/)
        parsed.pathname = parsed.path.replace(/(\?|#.*).*/, '')
        parsed.origin = parsed.protocol + '//' + parsed.host
        parsed.hashKey = hashs ? (hashs[2] || hashs[1] || '') : ''
        parsed.hashQuery = parseURLQuery(parsed.hash)
        parsed.searchQuery = parseURLQuery(parsed.search)
        return parsed
    }

    /**
     * 查询参数序列化
     *
     * @param {Object} query 序列化的对象
     */
    function serialize(query) {
        var _arr
        var params = []
        each(query, function (item, key) {
            if (!isUndefined(item)) {
                _arr = isArray(item)
                if (isPlainObject(item) || _arr) {
                    params = params.concat(stringifyParams(item, key, _arr))
                } else {
                    params.push(staticEncodeURIComponent(key) + '=' + staticEncodeURIComponent(isNull(item) ? '' : item))
                }
            }
        })
        return params.join('&').replace(/%20/g, '+')
    }

    /**
     * 查询参数序列化
     *
     * @param {String} query 反序列化的字符串
     */
    function unserialize(str) {
        var items
        var result = {}
        if (str && isString(str)) {
            arrayEach(str.split('&'), function (param) {
                items = param.split('=')
                result[staticDecodeURIComponent(items[0])] = staticDecodeURIComponent(items[1] || '')
            })
        }
        return result
    }

    /**
     * 获取地址栏信息
     *
     * @return Object
     */
    function locat() {
        return staticLocation ? parseUrl(staticLocation.href) : {}
    }

    /* eslint-disable valid-typeof */
    function isBrowseStorage(storage) {
        try {
            var testKey = '__xe_t'
            storage.setItem(testKey, 1)
            storage.removeItem(testKey)
            return true
        } catch (e) {
            return false
        }
    }

    function isBrowseType(type) {
        return navigator.userAgent.indexOf(type) > -1
    }

    /**
     * 获取浏览器内核
     * @return Object
     */
    function browse() {
        var $body, isChrome, isEdge
        var isMobile = false
        var result = {
            isNode: false,
            isMobile: isMobile,
            isPC: false,
            isDoc: !!staticDocument
        }
        if (!staticWindow && typeof process !== staticStrUndefined) {
            result.isNode = true
        } else {
            isEdge = isBrowseType('Edge')
            isChrome = isBrowseType('Chrome')
            isMobile = /(Android|webOS|iPhone|iPad|iPod|SymbianOS|BlackBerry|Windows Phone)/.test(navigator.userAgent)
            if (result.isDoc) {
                $body = staticDocument.body || staticDocument.documentElement
                arrayEach(['webkit', 'khtml', 'moz', 'ms', 'o'], function (core) {
                    result['-' + core] = !!$body[core + 'MatchesSelector']
                })
            }
            assign(result, {
                edge: isEdge,
                firefox: isBrowseType('Firefox'),
                msie: !isEdge && result['-ms'],
                safari: !isChrome && !isEdge && isBrowseType('Safari'),
                isMobile: isMobile,
                isPC: !isMobile,
                isLocalStorage: isBrowseStorage(staticWindow.localStorage),
                isSessionStorage: isBrowseStorage(staticWindow.sessionStorage)
            })
        }
        return result
    }

    /**
     * 返回一个获取对象属性的函数
     *
     * @param {String} name 属性名
     * @param {Object} defs 空值
     */
    function property(name, defs) {
        return function (obj) {
            return isNull(obj) ? defs : obj[name]
        }
    }

    function helperFixedNumber(str, digits) {
        return toValString(toNumber(str)).replace(new RegExp('(\\d+.\\d{0,' + digits + '}).*'), '$1')
    }

    function helperFormatEscaper(dataMap) {
        var replaceRegexp = new RegExp('(?:' + keys(dataMap).join('|') + ')', 'g')
        return function (str) {
            return toValString(str).replace(replaceRegexp, function (match) {
                return dataMap[match]
            })
        }
    }

    // ++++++++++++++string++++++++++
    /**
     * 转义HTML字符串，替换&, <, >, ", ', `字符
     *
     * @param {String} str 字符串
     * @return {String}
     */
    var escape = helperFormatEscaper(staticEscapeMap)

    var unescapeMap = {}
    each(staticEscapeMap, function (item, key) {
        unescapeMap[staticEscapeMap[key]] = key
    })

    /**
     * 反转escape
     *
     * @param {String} str 字符串
     * @return {String}
     */
    var unescape = helperFormatEscaper(unescapeMap)

    /**
     * 将带字符串转成驼峰字符串,例如： project-name 转为 projectName
     *
     * @param {String} str 字符串
     * @return {String}
     */
    function camelCase(str) {
        return toValString(str).replace(/(-[a-zA-Z])/g, function (text, u) {
            return u.substring(1).toLocaleUpperCase()
        })
    }


    // ++++++++++++++++++++++++++时间+++++++++++++++++++++

    /**
     * 将日期格式化为时间戳
     *
     * @param {String/Number/Date} str 日期或数字
     * @param {String} format 解析日期格式
     * @returns Number
     */
    function timestamp(date) {
        return date ? moment(date).valueOf() : moment().valueOf()
    }

    /**
     * 格式化日期
     *
     * @param {Number/String/Date} date 日期
     * @param {String} format 格式化
     */
    function toStringDate(date, format) {
        format = format || 'YYYY-MM-DD'
        return date ? moment(date).format(format) : moment().format(format);
    }

    /**
     * 开始结束时间
     * @param date {Number/String/Date} date 日期
     * @param dataType {String}  'year'年 'month'月 'week'周
     * @param format {String}  'year'年 'month'月 'week'周
     */
    function startDate(date, dataType, format) {
        format = format || 'YYYY-MM-DD';
        dataType = dataType || 'month';
        date = date ? moment(date) : moment();
        return date.startOf(dataType).format(format)
    }

    function endDate(date, dataType, format) {
        format = format || 'YYYY-MM-DD';
        dataType = dataType || 'month';
        date = date ? moment(date) : moment();
        return date.endOf(dataType).format(format)
    }

    /**
     * 时间计算 - +
     * @param date
     * @param number 计算值
     * @param dateType
     * @param format
     */
    function computeDate(date, number, dateType, format) {
        format = format || 'YYYY-MM-DD';
        dateType = dateType || 'days';
        date = date ? moment(date) : moment();
        return number > 0 ? date.add(Math.abs(number), dateType).format(format) :
            date.subtract(Math.abs(number), dateType).format(format);
    }

    /**
     * 生成时间列表
     * @param startDate 开始时间
     * @param endDate 结束时间
     * @param dateTytpe 时间类型
     * @param size  步长
     * @param format 时间格式
     * @returns {[]}
     */
    function listDate(startDate, endDate, dateTytpe, format) {
        var listDate1 = [];
        format = format || 'YYYY-MM-DD';
        dateTytpe = dateTytpe || 'day'
        startDate = moment(startDate);
        endDate = endDate ? moment(endDate) : moment();
        var dateLength = Math.abs(startDate.diff(endDate, dateTytpe));
        console.log(dateLength)
        listDate1.push(startDate.format(format))
        for (var i = 0; i < dateLength; i++) {
            listDate1.push(startDate.add(1, dateTytpe).format(format))
        }
        return listDate1
    }


    function toValString(obj) {
        if (isNumber(obj)) {
            if (('' + obj).indexOf('e-') >= 0) {
                var isNegative = obj < 0
                return (isNegative ? '-' : '') + '0' + ('' + ((isNegative ? Math.abs(obj) : obj) + 1)).substr(1)
            }
        }
        return '' + (eqNull(obj) ? '' : obj)
    }

    /**
     * layer
     */
    function alert(content, fun) {
        layer.alert(content, function (index) {
            //do something
            fun && fun();
            layer.close(index);
        });
    }

    function msg(content, fun) {
        layer.msg(content, {
            time: 2000
        });
    }

    function confirm(content, yes, no) {
        layer.open({
            content: content,
            btn: ['确认', '取消'],
            shadeClose: false,
            yes: yes,
            no: no
        });
    }

    /**
     * post
     * @param url
     * @param parms
     * @param fun
     * @param dataType
     */
    function post(url, parms, fun, dataType) {
        function addRequestKey(key) {
            requestList[key] = true;
        }

        // 将请求完成的api从记录中移除
        function removeRequestKey(key) {
            delete requestList[key];
        }

        //当前请求的api是否已有记录
        function hitRequestKey(key) {
            return requestList[key];
        }

        // 获取串行请求的key,方便记录
        function getLockRequestKey(key) {
            var ajaxKey = 'lockRequestKey:' + key;
            return JSON.stringify(ajaxKey);
        }

        var starttime = new Date().getTime();
        var ajaxKey = getLockRequestKey(url + JSON.stringify(parms))
        if (!dataType) {
            dataType = 'json';
        }
        if (hitRequestKey(ajaxKey)) {
            sl.msg('数据处理中...');
            return;
        }
        addRequestKey(ajaxKey);
        var ajaxcof = {
            url: url,    //请求的url地址
            dataType: dataType || "json",   //返回格式为json
            async: true,//请求是否异步，默认为异步，这也是ajax重要特性
            data: parms || {},    //参数值
            type: "POST",   //请求方式
        };
        //请求前的处理
        ajaxcof.beforeSend = function () {

        };
        ajaxcof.success = function (rs) {
            rs.latency = (new Date().getTime()) - starttime;
            if (rs.success) {
                fun(rs);
            } else {
                sl.msg(rs.msg);
            }
        };
        ajaxcof.complete = function () {
            // 请求完成后调用的回调函数（请求成功或失败时均调用）
            removeRequestKey(ajaxKey);
        };
        ajaxcof.error = function (XMLHttpRequest, textStatus, errorThrown) {
            sl.msg(textStatus)
        };
        $.ajax(ajaxcof)

    }

    function helperCreateIndexOf(name, callback) {
        return function (obj, val) {
            if (obj) {
                if (typeof obj === 'string' || isArray(obj)) {
                    if (obj[name]) {
                        return obj[name](val)
                    }
                    return callback(obj, val)
                }
                for (var key in obj) {
                    if (hasOwnProp(obj, key)) {
                        if (val === obj[key]) {
                            return key
                        }
                    }
                }
            }
            return -1
        }
    }

    function helperCreateInTypeof(type) {
        return function (obj) {
            return typeof obj === type
        }
    }

    var objectToString = Object.prototype.toString;

    function helperCreateInInObjectString(type) {
        return function (obj) {
            return '[object ' + type + ']' === objectToString.call(obj)
        }
    }

    function helperCreateMinMax(handle) {
        return function (arr, iterate) {
            if (arr && arr.length) {
                var rest, itemIndex
                arrayEach(arr, function (itemVal, index) {
                    if (iterate) {
                        itemVal = isFunction(iterate) ? iterate(itemVal, index, arr) : get(itemVal, iterate)
                    }
                    if (!eqNull(itemVal) && (eqNull(rest) || handle(rest, itemVal))) {
                        itemIndex = index
                        rest = itemVal
                    }
                })
                return arr[itemIndex]
            }
            return rest
        }
    }

    function helperCreateToNumber(handle) {
        return function (str) {
            if (str) {
                var num = handle(str)
                if (!isNaN(num)) {
                    return num
                }
            }
            return 0
        }
    }

    function objectEach(obj, iterate, context) {
        if (obj) {
            for (var key in obj) {
                if (hasOwnProp(obj, key)) {
                    iterate.call(context, obj[key], key, obj)
                }
            }
        }
    }

    function helperCreateiterateIndexOf(callback) {
        return function (obj, iterate, context) {
            if (obj && isFunction(iterate)) {
                if (isArray(obj) || isString(obj)) {
                    return callback(obj, iterate, context)
                }
                for (var key in obj) {
                    if (hasOwnProp(obj, key)) {
                        if (iterate.call(context, obj[key], key, obj)) {
                            return key
                        }
                    }
                }
            }
            return -1
        }
    }

    function helperCreatePickOmit(case1, case2) {
        return function (obj, callback) {
            var item, index
            var rest = {}
            var result = []
            var context = this
            var args = arguments
            var len = args.length
            if (!isFunction(callback)) {
                for (index = 1; index < len; index++) {
                    item = args[index]
                    result.push.apply(result, isArray(item) ? item : [item])
                }
                callback = 0
            }
            each(obj, function (val, key) {
                if ((callback ? callback.call(context, val, key, obj) : findIndexOf(result, function (name) {
                    return name === key
                }) > -1) ? case1 : case2) {
                    rest[key] = val
                }
            })
            return rest
        }
    }

    function helperDefaultCompare(v1, v2) {
        return v1 === v2
    }

    function helperDeleteProperty(obj, property) {
        try {
            delete obj[property]
        } catch (e) {
            obj[property] = undefined
        }
    }

    function helperEqualCompare(val1, val2, compare, func, key, obj1, obj2) {
        if (val1 === val2) {
            return true
        }
        if (val1 && val2 && !isNumber(val1) && !isNumber(val2) && !isString(val1) && !isString(val2)) {
            if (isRegExp(val1)) {
                return compare('' + val1, '' + val2, key, obj1, obj2)
            }
            if (isDate(val1) || isBoolean(val1)) {
                return compare(+val1, +val2, key, obj1, obj2)
            } else {
                var result, val1Keys, val2Keys
                var isObj1Arr = isArray(val1)
                var isObj2Arr = isArray(val2)
                if (isObj1Arr || isObj2Arr ? isObj1Arr && isObj2Arr : val1.constructor === val2.constructor) {
                    val1Keys = keys(val1)
                    val2Keys = keys(val2)
                    if (func) {
                        result = func(val1, val2, key)
                    }
                    if (val1Keys.length === val2Keys.length) {
                        return isUndefined(result) ? every(val1Keys, function (key, index) {
                            return key === val2Keys[index] && helperEqualCompare(val1[key], val2[val2Keys[index]], compare, func, isObj1Arr || isObj2Arr ? index : key, val1, val2)
                        }) : !!result
                    }
                    return false
                }
            }
        }
        return compare(val1, val2, key, obj1, obj2)
    }

    function deepClone(val, deep) {
        return isPlainObject(val) ? startClone(objectMap, val, deep) : isArray(val) ? startClone(map, val, deep) : val
    }

    assign(sl, {
        // object
        assign: assign,
        objectEach: objectEach,


        // array
        uniq: uniq,
        union: union,
        sortBy: sortBy,
        slice: slice,
        find: find,
        includes: includes,
        sum: sum,
        mean: mean,
        zip: zip,
        unzip: unzip,
        zipObject: zipObject,
        pluck: pluck,
        arrayEach: arrayEach,
        toArrayTree: toArrayTree,

        // base
        hasOwnProp: hasOwnProp,
        eqNull: eqNull,
        isNaN: isNumberNaN,
        isFinite: isNumberFinite,
        isUndefined: isUndefined,
        isArray: isArray,
        isFloat: isFloat,
        isInteger: isInteger,
        isFunction: isFunction,
        isBoolean: isBoolean,
        isString: isString,
        isNumber: isNumber,
        isRegExp: isRegExp,
        isObject: isObject,
        isPlainObject: isPlainObject,
        isDate: isDate,
        isEmpty: isEmpty,
        isNull: isNull,
        isMatch: isMatch,
        isEqual: isEqual,
        isEqualWith: isEqualWith,
        isMobile: isMobile,
        isCarNumber: isCarNumber,
        uniqueId: uniqueId,
        getSize: getSize,
        pick: pick,
        each: each,
        get: get,
        set: set,
        groupBy: groupBy,
        clone: clone,
        remove: remove,
        destructuring: destructuring,

        // number
        random: random,
        min: min,
        max: max,
        commafy: commafy,
        toFixedNumber: toFixedNumber,
        toNumber: toNumber,
        toInteger: toInteger,
        randomNum: randomNum,

        // date
        timestamp: timestamp,
        toStringDate: toStringDate,
        startDate: startDate,
        endDate: endDate,
        computeDate: computeDate,
        listDate: listDate,

        // string
        escape: escape,
        unescape: unescape,

        // url
        unserialize: unserialize,
        serialize: serialize,
        parseUrl: parseUrl,

        // browse
        locat: locat,
        browse: browse,

        //layer
        alert: alert,
        msg: msg,
        confirm: confirm,

        //post
        post: post
    })
    return sl;
}))
